﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.IO;

namespace GrapeCity.ColinHan.Reflactor
{
    public class RemoteAssemblyTraveller
    {
        public RemoteAssemblyTraveller()
        {
        }

        /// <summary>
        /// Reference search path used to search addition assemblies. For example: silverlight runtime assemblies.
        /// </summary>
        /// <remarks>
        /// If you want to use the traveller to travel in a silverlight assembly, you must add silverlight
        /// runtime path to this collection. if your silverlight assembly contains a dependency to sdk, you
        /// should include it too. follow is a sample list:
        ///    @"C:\Program Files (x86)\Microsoft Silverlight\5.0.60818.0",
        ///    @"C:\Program Files (x86)\Microsoft SDKs\Silverlight\v5.0\Libraries\Client"
        /// </remarks>
        public IEnumerable<string> ReferenceSearchPath { get; set; }

        private class RemoteWorker : MarshalByRefObject
        {
            public RemoteWorker()
            {
            }

            private AssemblyLoader _loader = new AssemblyLoader();

            public IEnumerable<string> ReferenceSearchPath
            {
                get { return _loader.ReferenceSearchPath; }
                set { _loader.ReferenceSearchPath = value; }
            }

            public void Travel(string assemblyFileName, Type travellerType, object[] args)
            {
                AssemblyVisitor traveller = Activator.CreateInstance(travellerType, args) as AssemblyVisitor;
                try
                {
                    traveller.Visit(_loader.LoadAssembly(assemblyFileName));
                }
                catch (Exception e)
                {
                    traveller.OnError(e);
                }
                finally
                {
                    traveller.OnFinished();
                }
            }
        }

        public void TravelIn(string assemblyFileName, Type visitorType, params object[] args)
        {
            RemoteWorker worker;
            AppDomain space = DomainHelper.CreateDomainForWorker("assemblyTravelSpace", out worker);

            List<string> searchPath = new List<string>();
            if (this.ReferenceSearchPath != null)
            {
                searchPath.AddRange(this.ReferenceSearchPath);
            }

            searchPath.Insert(0, Path.GetDirectoryName(assemblyFileName));
            worker.ReferenceSearchPath = searchPath;

            worker.Travel(assemblyFileName, visitorType, args);

            AppDomain.Unload(space);
        }
    }
}
